﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NetLib;
using System.Xml.Serialization;
using System.IO;
using FrameBuilderLib;

namespace FrameBuilderLib
{
    public static class Settings
    {
        public static bool concrete_concederation = true;
        public static bool synonims_concederation = true;
        public static bool context_concederation = true;
        public static int precent = 100;
    }

    public class CaseFrame
    {
        public Node GeneralNode;
        public List<List<string>> Words;
        public CaseFrame()
        {
            GeneralNode = new Node();
            Words = new List<List<string>>();
        }
    }

    public class Work_Net
    {
        public Net SemNet { get; set; }
        public List<int> MeasureNodes = new List<int>();
        public List<int> UsualCS = new List<int>();

        public void TypificationNodes()
        {
            int t;
            List<int> lid = (from c in SemNet.nodes where c.id < 0 select c.id).ToList();
            foreach (int i in lid)
            {
                t = Type_of_node(i);
                switch (t)
                {
                    case 1:// абстракное понятие
                        UsualCS.Add(i);
                        break;
                    case 2: // мера
                        MeasureNodes.Add(i);
                        break;
                    case 3: // обычная рамка
                        UsualCS.Add(i);
                        break;
                }

            }
        }

        /// <summary>
        /// получаем падежные рамки из начальной маркировки
        /// </summary>
        /// <returns></returns>
        public List<CaseFrame> Get_CaseFrames_Start()
        {

            List<CaseFrame> lcf = new List<CaseFrame>();
            List<int> sel_nodes;
            List<int> lid = new List<int>();

            TypificationNodes();

            sel_nodes = (from c in SemNet.nodes where (c.Marked) select c.id).ToList<int>();
            foreach (int id in sel_nodes)
            {
                if (id > 0)
                {
                    lid.AddRange(ConteinedCaseFrames(id));
                }
                else
                {
                    if (UsualCS.Contains(id))
                    {
                        lid.Add(id);
                    }
                    else // значит выделена мера
                    {
                        lid.AddRange(ConteinedCaseFrames(id));
                    }

                }
            }
            foreach (int id in lid)
            {
                CaseFrame cs = new CaseFrame();
                cs = Get_CaseFrame(id);
                if (cs != null)
                    lcf.Add(cs);
            }
            return lcf;
        }

        public List<int> ConteinedCaseFrames(int id)
        {
            List<int> lid = (from c in SemNet.links where (c.end_node.id == id) select c.start_node.id).ToList<int>();
            return lid;
        }


        public List<string> GetSynset(string mainname)
        {
            return (from c in SemNet.synsets where c.mainname == mainname select c.synlist).FirstOrDefault();
        }

        /// <summary>
        /// Получаем рамку из сети по id
        /// </summary>
        public CaseFrame Get_CaseFrame(int id)
        {
            CaseFrame cf = new CaseFrame();
            int k = FindNode(id);
            string str = "";
            List<string> list;
            string[] ar_str;
            if (k != -1)
            {
                cf.GeneralNode = SemNet.nodes[k];
                List<Link> links_out = new List<Link>();
                links_out = FindLink_Out_InFrame(id);
                foreach (Link l in links_out)
                {
                    if (l.end_node is NamedNode)
                    {

                        ar_str = (l.end_node as NamedNode).name.Split(' ');
                        foreach (string s in ar_str)
                        {
                            str += LemmAnylser.GetBaseWord(s) + " ";
                        }
                        str = str.TrimEnd(' ');
                        list = new List<string>();
                        list.Add(str);
                        List<string> syns = GetSynset((l.end_node as NamedNode).name) ?? new List<string>();
                        foreach (string syn in syns)
                        {
                            str = "";
                            ar_str = syn.Split(' ');
                            foreach (string s in ar_str)
                            {
                                str += LemmAnylser.GetBaseWord(s) + " ";
                            }
                            str = str.TrimEnd(' ');
                            list.Add(str);
                        }
                        
                       
                        cf.Words.Add(list);
                        str = "";

                    }
                    else // значит мы наткнулись на меру
                    {
                        List<Link> links_m = new List<Link>();
                        links_m = FindLink_Out_InFrame(l.end_node.id);
                        if ((links_m[0].end_node as NamedNode).name[0] != '#')
                        {
                            list = new List<string>();
                            list.Add((links_m[0].end_node as NamedNode).name);
                            cf.Words.Add(list);
                        }
                        list = new List<string>();
                        List<string> values = GetSynset((links_m[0].end_node as NamedNode).name);
                        int t = values.IndexOf((links_m[1].end_node as NamedNode).name);
                        if(t!=-1)
                        {
                            for(int i=t;i<values.Count;i++)
                            {
                                list.Add(values[i]);
                            }
                        }
                        cf.Words.Add(list);

                    }
                }
                return cf;

            }
            return null;

        }

        private int FindNode(int id)
        {
            for (int i = 0; i < SemNet.nodes.Count; i++)
            {
                if (id == SemNet.nodes[i].id) return i;
            }
            return -1;
        }
        /// <summary>
        /// получаем исходящие связи только внутри падежной рамки
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public List<Link> FindLink_Out_InFrame(int id)
        {
            return (from c in SemNet.links where (c.start_node.id == id) && ((c.end_node.id > 0) || (c.type.ToLower() == "мера")) select c).ToList<Link>();
        }
        /// <summary>
        /// получаем исходящие связи к другим падежным рамкам
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public List<Link> FindLink_Out_OutFrame(int id)
        {
            return (from c in SemNet.links where (c.start_node.id == id) && ((c.end_node.id < 0) && (c.type.ToLower() != "мера")) select c).ToList<Link>();
        }

        public List<Link> FindLink_In(int id)
        {
            return (from c in SemNet.links where (c.end_node.id == id) select c).ToList<Link>();
        }
        /// <summary>
        /// Определяет тип вершины
        /// </summary>
        /// <param name="id"></param>
        /// <returns>0-все плохо 1-абстракное понятие 3-общее понятие</returns>
        public int Type_of_node(int id)
        {
            List<Link> links;
            // определяем тип понятия, который ищем
            links = FindLink_Out_InFrame(id) ?? new List<Link>();
            return links.Count;
        }

        public List<Link> Find_aso(int id)
        {
            List<Link> links = new List<Link>();
            List<Link> help_links = new List<Link>();
            List<Link> links_aso = new List<Link>();
            List<Link> links_out = new List<Link>();
            List<Link> link_isa = new List<Link>();
            int i;

            links = FindLink_In(id);
            links_aso = (from c in links where c.type == "a_set_of" select c).ToList<Link>();
            links_out = FindLink_Out_OutFrame(id);
            link_isa = (from c in links_out where c.type == "is_a" select c).ToList<Link>();
            if (link_isa.Count > 0)
            {
                for (i = 0; i < link_isa.Count; i++)
                {
                    help_links = FindLink_Out_OutFrame(link_isa[i].end_node.id);
                    help_links = (from c in help_links where c.type == "a_set_of" select c).ToList<Link>();
                    links_aso.AddRange(help_links);
                }
            }
            return links_aso;
        }

        public List<Link> Find_apo(int id)
        {
            List<Link> links = new List<Link>();
            List<Link> help_links = new List<Link>();
            List<Link> links_apo = new List<Link>();
            List<Link> links_out = new List<Link>();
            List<Link> link_isa = new List<Link>();
            int i;

            links = FindLink_In(id);
            links_apo = (from c in links where c.type == "a_part_of" select c).ToList<Link>();
            links_out = FindLink_Out_OutFrame(id);
            link_isa = (from c in links_out where c.type == "is_a" select c).ToList<Link>();
            if (link_isa.Count > 0)
            {
                for (i = 0; i < link_isa.Count; i++)
                {
                    help_links = FindLink_In(link_isa[i].end_node.id);
                    help_links = (from c in help_links where c.type == "a_part_of" select c).ToList<Link>();
                    links_apo.AddRange(help_links);
                }
            }
            return links_apo;
        }

        public List<int> FindContext(int id)
        {
            List<int> lid,rez;
            rez=new List<int>();
            if(id<0)
            {
                lid = (from c in SemNet.links where (c.start_node.id == id) && (c.end_node.id > 0) select c.end_node.id).ToList();
                if(lid!=null)
                {
                    foreach (int i in lid)
                    {
                        rez.AddRange((from c in SemNet.links where (c.end_node.id == i)&&(c.start_node.id!=id) select c.start_node.id).ToList());
                    }
                }
            }
            return rez;
        }

        public List<int> FindGeneralization(int id)
        {
            List<Link> linkout = FindLink_Out_OutFrame(id);
            return (from c in linkout select c.end_node.id).ToList();

        }

        public Work_Net(string path)
        {
            XmlSerializer XmlSer = new XmlSerializer(typeof(Net));
            FileStream stream = new FileStream(path, FileMode.Open);
            SemNet = (Net)XmlSer.Deserialize(stream);
            stream.Close();
        }

    }


    public static class Validator
    {
        static public List<Mistake> mistakes = new List<Mistake>();

        private static List<int> checked_is_a_nodes = new List<int>();
        private static List<int> checked_apo_nodes = new List<int>();

        public static bool Validate(Work_Net net)
        {
            mistakes = new List<Mistake>();
            checked_is_a_nodes = new List<int>();
            checked_apo_nodes = new List<int>();
            bool res = true;
            List<Link> links;
            foreach (Node node in net.SemNet.nodes)
            {
                if (node.id < 0)
                {
                    links = net.FindLink_Out_InFrame(node.id);
                    switch (net.Type_of_node(node.id))
                    {
                        case 0:
                            mistakes.Add(new Mistake("Неправильно задана падежная рамка", "ПР" + (-node.id).ToString(), -node.id));
                            res = false;
                            break;
                        case 1:
                            //абстрактное понятие, ещё объект
                            if (links[0].type != "object")
                            {
                                mistakes.Add(new Mistake("Неправильно задана падежная рамка", "ПР" + (-node.id).ToString(), -node.id));
                                res = false;
                            }
                            break;
                        case 2:
                            //мера, ещё объект и значение
                            if ((from c in links where c.type == "object" select c).Count() != 1 ||
                                (from c in links where c.type == "value" select c).Count() != 1)
                            {
                                mistakes.Add(new Mistake("Неправильно задана падежная рамка", "ПР" + (-node.id).ToString(), -node.id));
                                res = false;
                            }
                            break;
                        case 3:
                            //понятие, ещё объект, значение и мена
                            if ((from c in links where c.type == "object" select c).Count() != 1 ||
                                (from c in links where c.type.ToLower().Trim() == "мера" select c).Count() != 1 ||
                                (from c in links where c.type == "is_a" select c).Count() != 1)
                            {
                                mistakes.Add(new Mistake("Неправильно задана падежная рамка", "ПР" + (-node.id).ToString(), -node.id));
                                res = false;
                            }
                            break;
                    }

                }
                else
                {
                    //именованная вершина
                    if ((node as NamedNode).name == null)
                    {
                        res = false;
                        mistakes.Add(new Mistake("Вершина не названа", "ИВ" + (-node.id).ToString(), node.id));
                    }
                }
            }
            bool res_is_a = true, res_apo = true;
            for (int i = 0; i < net.SemNet.nodes.Count && (res_apo || res_is_a); i++)
            {
                Node node = net.SemNet.nodes[i];
                if (node.id < 0)
                {

                    if (res_apo)
                    {
                        if (!checked_apo_nodes.Contains(node.id))
                        {
                            res_apo = Check_apo(net, node.id, net.FindLink_Out_OutFrame(node.id), new List<int>());
                        }
                    }
                    if (res_is_a)
                    {
                        if (!checked_is_a_nodes.Contains(node.id))
                        {
                            res_is_a = Check_is_a(net, node.id, net.FindLink_Out_OutFrame(node.id), new List<int>());
                        }
                    }
                }
            }
            if (!res_apo)
            {
                mistakes.Add(new Mistake("Присутствует цикл по a_part_of и a_set_of"));
                res = false;
            }
            if (!res_is_a)
            {
                mistakes.Add(new Mistake("Присутствует цикл по is_a"));
                res = false;
            }
            return res;
        }

        public static bool Check_apo(Work_Net net, int id_node, List<Link> links, List<int> WorkingMemory)
        {
            if (WorkingMemory.Contains(id_node))
                return false;
            checked_apo_nodes.Add(id_node);
            links = (from c in links where (c.type == "a_part_of" || c.type == "a_set_of") select c).ToList();
            bool res = true;
            if (links.Count > 0)
            {
                WorkingMemory.Add(id_node);
                foreach (Link link in links)
                {
                    if (res)
                        res = Check_apo(net, link.end_node.id, net.FindLink_Out_OutFrame(link.end_node.id), WorkingMemory);
                    else
                        return res;
                }
            }

            return res;
        }

        public static bool Check_is_a(Work_Net net, int id_node, List<Link> links, List<int> WorkingMemory)
        {
            if (WorkingMemory.Contains(id_node))
                return false;
            checked_is_a_nodes.Add(id_node);
            links = (from c in links where c.type == "is_a" select c).ToList();
            bool res = true;
            if (links.Count > 0)
            {
                WorkingMemory.Add(id_node);
                foreach (Link link in links)
                {
                    if (res)
                        res = Check_is_a(net, link.end_node.id, net.FindLink_Out_OutFrame(link.end_node.id), WorkingMemory);
                    else
                        return res;
                }
            }

            return res;
        }
    }
    public class Mistake
    {
        public string text { get; set; }
        public string node_name { get; set; }
        public int id { get; set; }
        public Mistake(string _text)
        {
            text = _text;
            id = 0;
        }
        public Mistake(string _text, string _name, int _id)
        {
            text = _text;
            node_name = _name;
            id = _id;
        }
    }
}
